# 题目四：使用米粒图像，分割得到各米粒，首先计算各区域(米粒)的面积、长度等信息，
# 进一步计算面积、长度的均值及方差，分析落在3sigma范围内米粒的数量。
import cv2 as cv
import numpy as np
import copy

filename = r"D:\AI\Picture\mini.png"
img = cv.imread(filename)
# 由于threshold只支持灰度图像，所以先转灰度图
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 使用大津算法阈值化
_, bw = cv.threshold(gray, 0, 0xff, cv.THRESH_OTSU)
# 形态学处理，去除噪声
element = cv.getStructuringElement(cv.MORPH_CROSS, (3, 3))
bw = cv.morphologyEx(bw, cv.MORPH_OPEN, element)

seg = copy.deepcopy(bw)
# 计算灰度直方图
bin, cnts, hier = cv.findContours(seg, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
count = 0

# 定义米粒长度列表
length_list = list()
# 定义米粒面积列表
area_list = list()
# 以下进行筛选
for i in range(1, len(cnts), 1):
    c = cnts[i]
    # 计算轮廓长度和面积并存进列表
    length = cv.arcLength(c, True)
    area = cv.contourArea(c)

    # 滤除面积小于10的分割结果：可能是噪声
    if area < 10:
        continue
    # 将合格的米粒存进列表
    length_list.append(length)
    area_list.append(area)
    # 统计米粒数量
    count = count + 1
    print("米粒: {}, 长度：{}, 面积：{}".format(i, length, area))

    # 计算轮廓包围就矩形
    x, y, w, h = cv.boundingRect(c)
    # 在原始图像上画出包围矩形，并给每个矩形标号
    cv.rectangle(img, (x, y), (x + w, y + h), (0, 0, 0xff), 1)
    cv.putText(img, str(count), (x, y), cv.FONT_HERSHEY_PLAIN, 0.5, (0, 0xff, 0))
# 求总面积：
all_area = np.sum(area_list)
# 求均值
length_mean = np.mean(length_list)
area_mean = np.mean(area_list)
# 求方差
length_var = np.var(length_list)
area_var = np.var(area_list)

print("合格的米粒数量：%d" % count)
print("合格的米粒总面积：%f" % all_area)
print("合格的米粒平均长度：%f" % length_mean)
print("合格的米粒平均面积：%f" % area_mean)
print("合格的米粒长度的方差：%f" % length_var)
print("合格的米粒面积的方差：%f" % area_var)

# 求面积标准差
arr_std = np.std(area_list, ddof=1)
# 求米粒面积在3sigma内的数量
area1 = area_mean - arr_std * 1.5
area2 = area_mean + arr_std * 1.5
print("面积3sigma的取值范围:", round(area1, 3), "--", round(area2, 3))
count1 = 0
for i in area_list:
    if area1 < i < area2:
        count1 = count1 + 1
print("米粒面积在3sigma内的数量为:", count1)

cv.imshow("source img", img)
cv.imshow("threshold img", bw)

cv.waitKey()
cv.destroyAllWindows()
